/**
 * 27/12/2013
 * 
 * PongGame
 * 
 * Copyright GPL
 */
package com.jtmproject;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector3;

/**
 * This class has the graphics engine
 * 
 * @author Pello Altadill && Javier Tejedor 
 */
public class PongGame implements ApplicationListener {
	

	private static final int MAX_TOUCH_ALLOWED = 20;

	public static final int SERVER = 1;
	public static final int CLIENT = 2;
	public static final int NOT_ONLINE = 0;
	
	private OrthographicCamera camera;
	private SpriteBatch batch;
	
	private Texture texturePaddle;
	private TextureRegion texturePaddleRegion;
	
	private Texture textureBall;
	private TextureRegion textureBallRegion;

	private Music backgroundMusic;

	private static World world = null;
	private Vector3[] pointOfTouch; 
	
	private BitmapFont font;
	private CharSequence textResult;
	
	private IClient iClient;
	private IServer iServer;
	
	private int typeGamer;
	
	/**
	 * constructor for not online game
	 */
	public PongGame(){
		this.typeGamer = NOT_ONLINE;
	}
	
	/**
	 * constructor for clients
	 * @param iClient
	 * @param typeGamer
	 */
	public PongGame(IClient iClient){
		this.iClient = iClient;
		this.typeGamer = CLIENT;
	}
	
	/**
	 * constructor for servers
	 * @param iServer
	 * @param typeGamer
	 */
	public PongGame(IServer iServer){
		this.iServer = iServer;
		this.typeGamer = SERVER;
	}
	
	@Override
	public void create() {	
		
		if(typeGamer == SERVER || typeGamer ==  NOT_ONLINE){
			world = new World(0, 0);
		}
		
		pointOfTouch = new Vector3[2];
		
		//to save the screen's point of touch 
		pointOfTouch[0] = new Vector3();
		pointOfTouch[1] = new Vector3();

		//from now on we use a screen of 100 x 150
		camera = new OrthographicCamera(GameValues.AXIS_X, GameValues.AXIS_Y);
		camera.position.set(GameValues.AXIS_X / 2, GameValues.AXIS_Y / 2, 0);
		camera.update();
		batch = new SpriteBatch();

		chargeTextures();
		chargeMusic();
		chargeFont();
		
		backgroundMusic.play();
		
	}

	/**
	 * this charge the font
	 */
	private void chargeFont() {
		font = new BitmapFont();
		font.setScale(0.5f);
	}

	/**
	 * this charges the music
	 */
	private void chargeMusic() {
		backgroundMusic = Gdx.audio.newMusic(GameValues.BACKGROUND_MUSIC);

		backgroundMusic.setVolume(GameValues.BACKGROUND_MUSIC_VOLUME);
		backgroundMusic.setLooping(true);
		
		GameSounds.setSounds();
	}

	/**
	 * this charges the textures
	 */
	private void chargeTextures() {
		
		//charge the textures
		texturePaddle = new Texture(GameValues.PADDLE_TEXTURE);
		texturePaddle.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		//select a texture region //new TextureRegion(x, y, sizeX, sizeY)
		texturePaddleRegion = new TextureRegion(texturePaddle, 0, 0, 64, 16);

		textureBall = new Texture(GameValues.BALL_TEXTURE);
		textureBall.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		textureBallRegion = new TextureRegion(textureBall, 0, 0, 16, 16);
	}

	@Override
	public void render() {
		
		if(world != null){
			//if the screen has been touched
			if (Gdx.input.isTouched()){
				
				switch(typeGamer){
				case NOT_ONLINE:
					notOnlineGame();
					
					break;
					
				case SERVER:
					serverGame();
					
					break;
					
				case CLIENT:
					clientGame();
					
					break;
				}

			}

			//clear the screen. It paints all the screen with black
			Gdx.gl.glClearColor(0, 0, 0, 0);
			Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
			
			//It applies the camera to the batch
			batch.setProjectionMatrix(camera.combined);

			batch.begin();

			if (world.isGameOver()) {
				int goalsPlayerA = world.getPaddleGamerA().getGoalsTaked();
				int goalsPlayerB = world.getPaddleGamerB().getGoalsTaked();
				world = new World(goalsPlayerA, goalsPlayerB);
				
			} else {
				paintResult();
				world.update(Gdx.graphics.getDeltaTime());
				paint();
		
			}
			
			batch.end();
		}
		
		if(typeGamer == SERVER){
			world.getPaddleGamerB().move(iServer.getMoveXB());
			iServer.sendClassWorldSerialized(world);
		}
		
	}

	/**
	 * this analyzes the client's game touches
	 */
	private void clientGame() {
		camera.unproject(pointOfTouch[0].set(
				Gdx.input.getX(0), Gdx.input.getY(0), 0));

		if(pointOfTouch[0].y > 80 && pointOfTouch[0].x < 95){
			//world.getPaddleGamerB().move(pointOfTouch[0].x - 20);
			float number = 0.0f;
			number = pointOfTouch[0].x - 20;
			String num = "";
			num = String.valueOf(number);
			Gdx.app.log("TEJE", num);
			iClient.sendMessage(num);
		}
	}

	/**
	 * this analyzes the server's game touches
	 */
	private void serverGame() {
		camera.unproject(pointOfTouch[0].set(
				Gdx.input.getX(0), Gdx.input.getY(0), 0));

		if(pointOfTouch[0].y < 60 && pointOfTouch[0].x < 95){
			world.getPaddleGamerA().move(pointOfTouch[0].x - 20);
			
		}
	}

	/**
	 * this analyzes the "not online" touches
	 */
	private void notOnlineGame() {
		int numberOfTouchedAtTheSameTime = 0;
		//this counts the number of the touched at the same time
		for(int i = 0 ; i < MAX_TOUCH_ALLOWED; i++) {
			if(Gdx.input.isTouched(i)) {
				if(Gdx.input.getY(i) < 60 || Gdx.input.getY() > 80){
					numberOfTouchedAtTheSameTime++;
				}
			}
		}

		switch (numberOfTouchedAtTheSameTime) {
		case 1:

			serverGame();
			
			if(pointOfTouch[0].y > 80 && pointOfTouch[0].x < 95){
				world.getPaddleGamerB().move(pointOfTouch[0].x - 20);
			}
			break;

		case 2:

			for(int j = 0; j < numberOfTouchedAtTheSameTime; j++) {
				camera.unproject(pointOfTouch[j].set(
						Gdx.input.getX(j), Gdx.input.getY(j), 0));
			}

			if(pointOfTouch[0].y < 60 && pointOfTouch[0].x < 95){
				world.getPaddleGamerA().move(pointOfTouch[0].x - 20);
			}else{
				world.getPaddleGamerA().move(pointOfTouch[1].x - 20);
			}

			if(pointOfTouch[1].y > 80 && pointOfTouch[1].x < 95){
				world.getPaddleGamerB().move(pointOfTouch[1].x - 20);
			}else{
				world.getPaddleGamerB().move(pointOfTouch[0].x - 20);
			}
		default:
			//nothing to do. Only the game will work with one or two touches
			//at the same time
			break;
		}
	}

	/**
	 * this paints the result into the screen 
	 */
	private void paintResult() {
		font.setColor(Color.WHITE);
		textResult = world.getPaddleGamerB().getGoalsTaked()
				+ " - " + world.getPaddleGamerA().getGoalsTaked();
		//to center the text into the screen
		font.draw(batch, textResult, 45, 80);
	}

	/**
	 * this paint the world
	 */
	private void paint() {
		batch.draw(
				textureBallRegion, 
				world.getBall().getCurrentPosition().x,
				world.getBall().getCurrentPosition().y,
				5f,
				5f);
		
		batch.draw(
				texturePaddleRegion, 
				world.getPaddleGamerA().getPosition().x,
				world.getPaddleGamerA().getPosition().y, 
				30, 
				5f);
		
		batch.draw(
				texturePaddleRegion, 
				world.getPaddleGamerB().getPosition().x,
				world.getPaddleGamerB().getPosition().y, 
				30, 
				5f);
	}

	@Override
	public void dispose() {
		batch.dispose();
		texturePaddle.dispose();
		textureBall.dispose();
		
		switch (typeGamer) {
		case CLIENT:
			
			iClient.cancelConnection();
			
			break;
		case SERVER:
			
			iServer.sendCancelConnection();
			iServer.cancelConnection();
			
			break;
		default:
			break;
		}
	}

	@Override
	public void resize(int width, int heigcht) {
		
	}

	@Override
	public void pause() {
		
	}

	@Override
	public void resume() {
		
	}

	/**
	 * return the static world
	 * @return
	 */
	public static World getWorld() {
		return world;
	}

	/**
	 * set the static world
	 * @param world
	 */
	public static void setWorld(World world) {
		PongGame.world = world;
	}

}
